/*	--- 中断线程 --- 


如果线程需要执行一个长时间任务，就可能需要能中断线程。

【中断线程】就是'其他线程'给'该线程'发一个【信号】, 该线程收到【信号】后结束执行run()方法, 使得自身线程能立刻结束运行。

我们举个栗子：假设从网络下载一个100M的文件，如果网速很慢，用户等得不耐烦，就可能在下载过程中点“取消”，这时，程序就需要中断下载线程的执行。

中断一个线程非常简单，只需要在'其他线程'中对'目标线程'调用interrupt()方法，'目标线程'需要反复检测自身状态是否是interrupted状态，如果是，就立刻结束运行。

我们还是看示例代码：	*/

// 中断线程
public class Main {
	public static void main(String[] args) throws InterruptedException {
		Thread t = new MyThread();
		t.start();

		Thread.sleep(1); // 暂停1ms
		t.interrupt(); // 中断t线程
		t.join(); // 等待t线程结束
		System.out.println("end");
	}
}

class MyThread extends Thread {
	public void run() {
		int n = 0;

		while (! isInterrupted()) {
			n++;
			System.out.println(n + " hello !");
		}
	}
}


/*
仔细看上述代码，main线程通过调用t.interrupt()方法中断t线程.

但是要注意，interrupt()方法仅仅向t线程发出了“中断请求”，至于t线程是否能立刻响应，要看具体代码。

而t线程的while循环会检测isInterrupted()，所以上述代码能正确响应interrupt()请求，使得自身立刻结束运行run()方法。

------------------------

如果线程处于等待状态，例如，t.join()会让main线程进入等待状态，此时，如果对main线程调用interrupt(), 那么join()方法会立刻抛出InterruptedException。

因此，'目标线程'只要捕获到join()方法抛出的InterruptedException，就说明有其他线程对其调用了interrupt()方法，通常这样情况下'该线'程应该立刻结束运行。

我们来看下面的示例代码：  */
public class Main {
	public static void main(String[] args) throws InterruptedException {
		Thread t = new MyThread();
		t.start();
		Thread.sleep(1000);

		t.interrupt(); // 中断t线程
		t.join(); // 等待t线程结束

		System.out.println("end");
	}
}

class MyThread extends Thread {
	public void run() {
		Thread hello = new HelloThread();
		hello.start(); // 启动hello线程

		try {
			hello.join(); // 等待hello线程结束
		} catch (InterruptedException e) {
			System.out.println("interrupted !");
		}

		hello.interrupt();
	}
}

class HelloThread extends Thread {
	public void run() {
		int n = 0;

		while (!isInterrupted()) {
			n++;
			System.out.println(n + " hello !");

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				break;
			}
		}
	}
}


/*
main线程通过调用t.interrupt()从而通知t线程中断，而此时t线程正位于hello.join()的等待中，此方法会立刻结束t线程对hello线程的等待，并且使t线程抛出InterruptedException。

由于我们在t线程中捕获了InterruptedException，因此，就可以准备结束该线程。

在t线程结束前，对hello线程也进行了interrupt()调用通知其中断。如果去掉这一行代码，可以发现hello线程仍然会继续运行，且JVM不会退出。(如果缺少了！isInterrupted()则会成为'野线程')

另一个常用的中断线程的方法是【设置标志位】。

我们通常会用一个'running标志位'来标识【线程】是否应该继续运行，在外部线程中，通过把HelloThread.running置为false，就可以让线程结束： 

标志位 = 'volatile'(关键字)
	adj.  易变的;无定性的;无常性的;可能急剧波动的;不稳定的;易恶化的;易挥发的;易发散的
*/



// 中断线程
public class Main {
	public static void main(String[] args) throws InterruptedException {
		HelloThread t = new HelloThread();
		t.start();
		Thread.sleep(1);

		t.running = false; // 标志位置为false
	}
}

class HelloThread extends Thread {
	public volatile boolean running = true; // volatile

	public void run() {
		int n = 0;

		while (running) {
			n++;
			System.out.println(n + " hello !");
		}

		System.out.println("end !");
	}
}

/*
注意到HelloThread的标志位
	public volatile boolean running
是一个线程间共享的变量。

【线程间共享变量】需要使用volatile关键字标记，确保'每个线程'都能读取到更新后的变量值。


----------------------

Q: 为什么要对【线程间共享的变量】用'关键字volatile'声明？

A: 这涉及到Java的内存模型。

在Java虚拟机中，变量的值 保存在 主内存中(运行内存).

但是，当线程访问变量时，它会先获取一个副本，并保存在自己的工作内存中。如果线程修改了变量的值，虚拟机会在某个时刻把修改后的值回写到主内存，但是，这个时间是不确定的！


	┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
	        Main Memory(运行内存)
	│                               │
	   ┌───────┐┌───────┐┌───────┐
	│  │ var A ││ var B ││ var C │  │
	   └───────┘└───────┘└───────┘
	│     │ ▲               │ ▲     │
	 ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─
	      │ │               │ │ (can't ensure time)
	┌ ─ ─ ┼ ┼ ─ ─ ┐   ┌ ─ ─ ┼ ┼ ─ ─ ┐
	      ▼ │               ▼ │
	│  ┌───────┐  │   │  ┌───────┐  │
	   │ var A │         │ var C │
	│  └───────┘  │   │  └───────┘  │
	   Thread 1          Thread 2
	└ ─ ─ ─ ─ ─ ─ ┘   └ ─ ─ ─ ─ ─ ─ ┘


这会导致如果一个线程更新了某个变量，另一个线程读取的值可能还是更新前的。

例如，主内存的 变量a = true，

线程1执行a = false时，它在此刻仅仅是把'变量a的副本'变成了false，
主内存的 变量a 还是 true，

在JVM把修改后的a回写到主内存之前，其他线程读取到的a的值仍然是true，这就造成了多线程之间共享的变量不一致。


--------------------------


因此，volatile关键字的目的是告诉虚拟机：

	· 每次访问变量时，总是获取主内存的最新值；
	· 每次修改变量后，立刻回写到主内存。(这个才是重点,立即回写)

'volatile关键字'解决的是可见性问题：当一个线程修改了某个共享变量的值,'其他线程'能够立刻看到修改后的值。


如果我们去掉volatile关键字，运行上述程序，发现效果和带volatile差不多

这是因为在x86的架构下，JVM回写主内存的速度非常快，但是，换成ARM的架构，就会有显著的延迟。


*/




/*	---- 中断线程 の 小结 ---- 

1. 对目标线程调用interrupt()方法可以请求中断一个线程，目标线程通过检测isInterrupted()标志获取自身是否已中断。如果目标线程处于等待状态Thread.join();， 该线程会捕获到InterruptedException；

	t.interrupt(); // 中断t线程


2. 目标线程检测到isInterrupted()为true或者捕获了InterruptedException都应该立刻结束自身线程；
	
	public void run() {
		Thread hello = new HelloThread();
		hello.start(); // 启动hello线程

		try {
			hello.join(); // 等待hello线程结束
		} catch (InterruptedException e) {
			System.out.println("interrupted !");
		}
	}


3. 通过标志位判断需要正确使用volatile关键字；
	
	t.running = false; // 标志位置为false



4. volatile关键字解决了共享变量在线程间的可见性问题。

	public volatile boolean running = true; // volatile

*/









